namespace C1.Win.C1FlexGrid
{
    using System;
    using System.Collections;
    using System.Drawing;

    public class Node
    {
        private C1.Win.C1FlexGrid.Row _row;

        internal Node(C1.Win.C1FlexGrid.Row row)
        {
            if (!row.IsNode)
            {
                throw new ArgumentException("Source Row is not a node");
            }
            this._row = row;
        }

        public Node AddNode(NodeTypeEnum Position, object Data)
        {
            return this.AddNode(Position, Data, null, null);
        }

        public Node AddNode(NodeTypeEnum Position, object Data, object Key, System.Drawing.Image Img)
        {
            if ((Position == NodeTypeEnum.Root) || (Position == NodeTypeEnum.Parent))
            {
                throw new ArgumentException("Cannot add Parent/Root nodes");
            }
            GridModel model = this._row._coll._grid;
            int num = this._row._nodeLevel;
            if ((Position == NodeTypeEnum.LastChild) || (Position == NodeTypeEnum.FirstChild))
            {
                num++;
            }
            bool flag = false;
            int nodeRow = this.GetNodeRow(Position);
            switch (Position)
            {
                case NodeTypeEnum.FirstChild:
                    if (nodeRow < 0)
                    {
                        flag = true;
                    }
                    break;

                case NodeTypeEnum.LastChild:
                    flag = true;
                    break;

                case NodeTypeEnum.LastSibling:
                    flag = true;
                    break;

                case NodeTypeEnum.NextSibling:
                    if (nodeRow < 0)
                    {
                        flag = true;
                    }
                    break;

                case NodeTypeEnum.PreviousSibling:
                    if (nodeRow >= 0)
                    {
                        flag = true;
                    }
                    break;
            }
            if (nodeRow < 0)
            {
                nodeRow = this._row.SafeIndex;
            }
            if (nodeRow < 0)
            {
                throw new ArgumentException("Cannot find location for new node.");
            }
            if (flag)
            {
                Point rowSpan = model._rows[nodeRow].Node.GetRowSpan();
                nodeRow = (rowSpan.Y > -1) ? (rowSpan.Y + 1) : (nodeRow + 1);
            }
            Node node = model._rows.InsertInternal(nodeRow, false, true).Node;
            node.Level = num;
            node.Data = Data;
            if (Img != null)
            {
                node.Image = Img;
            }
            if (Key != null)
            {
                node.Key = Key;
            }
            if ((nodeRow > 0) && !model._rows[nodeRow - 1].Visible)
            {
                node.Collapsed = true;
            }
            return node;
        }

        public void EnsureVisible()
        {
            for (Node node = this.GetNode(NodeTypeEnum.Parent); node != null; node = node.GetNode(NodeTypeEnum.Parent))
            {
                if (node.Collapsed)
                {
                    node.Collapsed = false;
                }
            }
            GridModel model = this._row._coll._grid;
            int safeIndex = this._row.SafeIndex;
            int column = model._tree.Column;
            model.NotifyViews(GridChangedTypeEnum.EnsureVisible, safeIndex, column);
        }

        public CellRange GetCellRange()
        {
            GridModel grid = this._row._coll._grid;
            Point rowSpan = this.GetRowSpan();
            rowSpan.X = this._row.SafeIndex;
            if (rowSpan.Y < 0)
            {
                rowSpan.Y = rowSpan.X;
            }
            return new CellRange(grid, rowSpan.X, 0, rowSpan.Y, grid._cols.Count - 1);
        }

        internal int GetChildRow(int child)
        {
            RowCollection rows = this._row._coll._grid._rows;
            int num = this._row._nodeLevel + 1;
            Point rowSpan = this.GetRowSpan();
            if (rowSpan.X >= 0)
            {
                for (int i = rowSpan.X; i <= rowSpan.Y; i++)
                {
                    if (rows[i].IsNode && (rows[i]._nodeLevel <= num))
                    {
                        if (child == 0)
                        {
                            return i;
                        }
                        child--;
                    }
                }
            }
            return -1;
        }

        public Node GetNode(NodeTypeEnum Position)
        {
            GridModel model = this._row._coll._grid;
            int nodeRow = this.GetNodeRow(Position);
            if (nodeRow < 0)
            {
                return null;
            }
            return new Node(model._rows[nodeRow]);
        }

        internal int GetNodeRow(NodeTypeEnum Position)
        {
            RowCollection collection1 = this._row._coll._grid._rows;
            int safeIndex = this._row.SafeIndex;
            short num2 = this._row._nodeLevel;
            switch (Position)
            {
                case NodeTypeEnum.Root:
                    return this.GetParentRow(true);

                case NodeTypeEnum.Parent:
                    return this.GetParentRow(false);

                case NodeTypeEnum.FirstChild:
                    return this.GetChildRow(0);

                case NodeTypeEnum.LastChild:
                    return this.GetChildRow(this.Children - 1);

                case NodeTypeEnum.FirstSibling:
                    return this.GetSiblingRow(Position);

                case NodeTypeEnum.LastSibling:
                    return this.GetSiblingRow(Position);

                case NodeTypeEnum.NextSibling:
                    return this.GetSiblingRow(Position);

                case NodeTypeEnum.PreviousSibling:
                    return this.GetSiblingRow(Position);
            }
            return -1;
        }

        internal int GetParentRow(bool root)
        {
            GridModel model = this._row._coll._grid;
            RowCollection rows = model._rows;
            int num = this._row._nodeLevel;
            if (model.SubtotalPosition == SubtotalPositionEnum.AboveData)
            {
                for (int i = this._row.SafeIndex - 1; i >= 0; i--)
                {
                    if (rows[i].IsNode)
                    {
                        int num3 = rows[i]._nodeLevel;
                        if (num3 < num)
                        {
                            if (!root)
                            {
                                return i;
                            }
                            if (num3 == rows._minNodeLevel)
                            {
                                return i;
                            }
                        }
                    }
                }
            }
            else
            {
                for (int j = this._row.SafeIndex + 1; j < rows.Count; j++)
                {
                    if (rows[j].IsNode)
                    {
                        int num5 = rows[j]._nodeLevel;
                        if (num5 < num)
                        {
                            if (!root)
                            {
                                return j;
                            }
                            if (num5 == rows._minNodeLevel)
                            {
                                return j;
                            }
                        }
                    }
                }
            }
            return -1;
        }

        internal Point GetRowSpan()
        {
            return GetRowSpan(this._row._coll._grid, this._row.SafeIndex);
        }

        internal static Point GetRowSpan(GridModel g, int index)
        {
            Point point = new Point(-1, -1);
            RowCollection rows = g._rows;
            if (rows[index].IsNode)
            {
                if (g._subtotPos == SubtotalPositionEnum.AboveData)
                {
                    if (index < (rows.Count - 1))
                    {
                        int num = rows[index]._nodeLevel;
                        if (rows[index + 1].IsNode && (rows[index + 1]._nodeLevel <= num))
                        {
                            return point;
                        }
                        point.X = point.Y = index + 1;
                        for (int j = point.X; j < rows.Count; j++)
                        {
                            if (rows[j].IsNode && (rows[j]._nodeLevel <= num))
                            {
                                return point;
                            }
                            point.Y = j;
                        }
                    }
                    return point;
                }
                if (index <= rows.Fixed)
                {
                    return point;
                }
                int num3 = rows[index]._nodeLevel;
                if (rows[index - 1].IsNode && (rows[index - 1]._nodeLevel <= num3))
                {
                    return point;
                }
                point.X = point.Y = index - 1;
                for (int i = point.X; i >= rows.Fixed; i--)
                {
                    if (rows[i].IsNode && (rows[i]._nodeLevel <= num3))
                    {
                        return point;
                    }
                    point.X = i;
                }
            }
            return point;
        }

        internal int GetSiblingRow(NodeTypeEnum Position)
        {
            RowCollection rows = this._row._coll._grid._rows;
            int num = this._row._nodeLevel;
            int safeIndex = this._row.SafeIndex;
            int num3 = -1;
            switch (Position)
            {
                case NodeTypeEnum.FirstSibling:
                    safeIndex--;
                    while (safeIndex >= 0)
                    {
                        if (rows[safeIndex].IsNode)
                        {
                            int num6 = rows[safeIndex]._nodeLevel;
                            if (num6 <= num)
                            {
                                if (num6 < num)
                                {
                                    return num3;
                                }
                                num3 = safeIndex;
                            }
                        }
                        safeIndex--;
                    }
                    return num3;

                case NodeTypeEnum.LastSibling:
                    while (safeIndex < rows.Count)
                    {
                        if (rows[safeIndex].IsNode)
                        {
                            int num7 = rows[safeIndex]._nodeLevel;
                            if (num7 <= num)
                            {
                                if (num7 < num)
                                {
                                    return num3;
                                }
                                num3 = safeIndex;
                            }
                        }
                        safeIndex++;
                    }
                    return num3;

                case NodeTypeEnum.NextSibling:
                    safeIndex++;
                    while (safeIndex < rows.Count)
                    {
                        if (rows[safeIndex].IsNode)
                        {
                            int num4 = rows[safeIndex]._nodeLevel;
                            if (num4 <= num)
                            {
                                if (num4 < num)
                                {
                                    return -1;
                                }
                                return safeIndex;
                            }
                        }
                        safeIndex++;
                    }
                    break;

                case NodeTypeEnum.PreviousSibling:
                    safeIndex--;
                    while (safeIndex >= 0)
                    {
                        if (rows[safeIndex].IsNode)
                        {
                            int num5 = rows[safeIndex]._nodeLevel;
                            if (num5 <= num)
                            {
                                if (num5 < num)
                                {
                                    return -1;
                                }
                                return safeIndex;
                            }
                        }
                        safeIndex--;
                    }
                    break;
            }
            return -1;
        }

        public bool Move(NodeMoveEnum MoveTo)
        {
            return this.Move(MoveTo, null);
        }

        public bool Move(NodeMoveEnum MoveTo, Node TargetNode)
        {
            int num;
            int num2;
            Point rowSpan;
            RowCollection rows = this._row._coll._grid._rows;
            switch (MoveTo)
            {
                case NodeMoveEnum.In:
                    if (this.GetNodeRow(NodeTypeEnum.PreviousSibling) >= 0)
                    {
                        rowSpan = this.GetRowSpan();
                        for (num = rowSpan.X; (num <= rowSpan.Y) && (rowSpan.Y > -1); num++)
                        {
                            if (rows[num].IsNode)
                            {
                                C1.Win.C1FlexGrid.Row row1 = rows[num];
                                row1._nodeLevel = (short) (row1._nodeLevel + 1);
                            }
                        }
                        this.Level++;
                        break;
                    }
                    return false;

                case NodeMoveEnum.Out:
                    if (this.GetNodeRow(NodeTypeEnum.Parent) >= 0)
                    {
                        rowSpan = this.GetRowSpan();
                        for (num = rowSpan.X; (num <= rowSpan.Y) && (rowSpan.Y > -1); num++)
                        {
                            if (rows[num].IsNode)
                            {
                                C1.Win.C1FlexGrid.Row row2 = rows[num];
                                row2._nodeLevel = (short) (row2._nodeLevel - 1);
                            }
                        }
                        this.Level--;
                        break;
                    }
                    return false;

                case NodeMoveEnum.Up:
                case NodeMoveEnum.First:
                    num = (MoveTo == NodeMoveEnum.Up) ? this.GetNodeRow(NodeTypeEnum.PreviousSibling) : this.GetNodeRow(NodeTypeEnum.FirstSibling);
                    if (num >= 0)
                    {
                        rowSpan = this.GetRowSpan();
                        rowSpan.X = this._row.SafeIndex;
                        if (rowSpan.Y < 0)
                        {
                            rowSpan.Y = rowSpan.X;
                        }
                        num2 = (rowSpan.Y - rowSpan.X) + 1;
                        rows.MoveRange(rowSpan.X, num2, num);
                        break;
                    }
                    return false;

                case NodeMoveEnum.Down:
                case NodeMoveEnum.Last:
                    num = (MoveTo == NodeMoveEnum.Down) ? this.GetNodeRow(NodeTypeEnum.NextSibling) : this.GetNodeRow(NodeTypeEnum.LastSibling);
                    if (num >= 0)
                    {
                        rowSpan = rows[num].Node.GetRowSpan();
                        num = (rowSpan.Y < 0) ? (num + 1) : (rowSpan.Y + 1);
                        rowSpan = this.GetRowSpan();
                        rowSpan.X = this._row.SafeIndex;
                        if (rowSpan.Y < 0)
                        {
                            rowSpan.Y = rowSpan.X;
                        }
                        num2 = (rowSpan.Y - rowSpan.X) + 1;
                        num -= num2;
                        rows.MoveRange(rowSpan.X, num2, num);
                        break;
                    }
                    return false;

                case NodeMoveEnum.ChildOf:
                {
                    if (TargetNode == null)
                    {
                        throw new ArgumentException("Need target node argument for this type of move.");
                    }
                    if (TargetNode._row == this._row)
                    {
                        return false;
                    }
                    if (TargetNode == this.GetNode(NodeTypeEnum.Parent))
                    {
                        return false;
                    }
                    rowSpan = this.GetRowSpan();
                    rowSpan.X = this._row.SafeIndex;
                    if (rowSpan.Y < 0)
                    {
                        rowSpan.Y = rowSpan.X;
                    }
                    num = TargetNode._row.SafeIndex + 1;
                    if ((num >= rowSpan.X) && (num <= rowSpan.Y))
                    {
                        return false;
                    }
                    short num3 = (short) ((TargetNode.Level + 1) - this.Level);
                    for (int i = rowSpan.X; i <= rowSpan.Y; i++)
                    {
                        if (rows[i].IsNode)
                        {
                            C1.Win.C1FlexGrid.Row row3 = rows[i];
                            row3._nodeLevel = (short) (row3._nodeLevel + num3);
                        }
                    }
                    num2 = (rowSpan.Y - rowSpan.X) + 1;
                    if (rowSpan.X < num)
                    {
                        num -= num2;
                    }
                    rows.MoveRange(rowSpan.X, num2, num);
                    break;
                }
            }
            return true;
        }

        public void RemoveNode()
        {
            Point rowSpan = this.GetRowSpan();
            rowSpan.X = this._row.SafeIndex;
            if (rowSpan.Y < 0)
            {
                rowSpan.Y = rowSpan.X;
            }
            GridModel model = this._row._coll._grid;
            bool redraw = model.Redraw;
            model.Redraw = false;
            try
            {
                for (int i = rowSpan.X; i <= rowSpan.Y; i++)
                {
                    model._rows.RemoveInternal(rowSpan.X, false);
                }
            }
            finally
            {
                model.Redraw = redraw;
            }
        }

        public void Select()
        {
            this.EnsureVisible();
            GridModel model = this._row._coll._grid;
            int safeIndex = this._row.SafeIndex;
            int column = model._tree.Column;
            model.NotifyViews(GridChangedTypeEnum.Select, safeIndex, column);
        }

        public void Sort(SortFlags order)
        {
            int column = this._row._coll._grid._tree.Column;
            this.Sort(order, column, column);
        }

        public void Sort(SortFlags order, int col1, int col2)
        {
            Node nd = this.GetNode(NodeTypeEnum.FirstChild);
            if (nd != null)
            {
                SortSiblings(nd, order, col1, col2);
            }
        }

        internal static void SortSiblings(Node nd, SortFlags order, int col1, int col2)
        {
            GridModel grid = nd.Row._coll._grid;
            grid.AssertNotBound();
            ArrayList list = new ArrayList();
            while (nd != null)
            {
                list.Add(nd.Row);
                nd = nd.GetNode(NodeTypeEnum.NextSibling);
            }
            GridRowComparer comparer = new GridRowComparer(grid, order, col1, col2);
            list.Sort(comparer);
            for (int i = list.Count - 1; i >= 0; i--)
            {
                nd = ((C1.Win.C1FlexGrid.Row) list[i]).Node;
                nd.Move(NodeMoveEnum.First);
            }
        }

        public int Children
        {
            get
            {
                Point rowSpan = this.GetRowSpan();
                if ((rowSpan.X < 0) || (rowSpan.Y < 0))
                {
                    return 0;
                }
                int num = 0;
                int num2 = this._row._nodeLevel + 1;
                GridModel model = this._row._coll._grid;
                for (int i = rowSpan.X; i <= rowSpan.Y; i++)
                {
                    if (model._rows[i].IsNode && (model._rows[i]._nodeLevel == num2))
                    {
                        num++;
                    }
                }
                return num;
            }
        }

        public bool Collapsed
        {
            get
            {
                return (((short) (this._row._flags & RowColFlags.Collapsed)) != 0);
            }
            set
            {
                if (this.Collapsed != value)
                {
                    int safeIndex = this._row.SafeIndex;
                    GridModel model = this._row._coll._grid;
                    model.NotifyViews(GridChangedTypeEnum.BeforeCollapse, safeIndex, -1);
                    if (!model._tree._cancel)
                    {
                        Point rowSpan = this.GetRowSpan();
                        if (rowSpan.X < 0)
                        {
                            if (this.Collapsed)
                            {
                                this._row.SetFlags(RowColFlags.Collapsed, false);
                                model.NotifyViews(GridChangedTypeEnum.RepaintGrid);
                            }
                        }
                        else
                        {
                            this._row.SetFlags(RowColFlags.Collapsed, value);
                            RowCollection rows = this._row._coll._grid._rows;
                            if (model.SubtotalPosition == SubtotalPositionEnum.AboveData)
                            {
                                for (int i = rowSpan.X; i <= rowSpan.Y; i++)
                                {
                                    C1.Win.C1FlexGrid.Row row = rows[i];
                                    row.SetFlags(RowColFlags.Hidden, value);
                                    if ((!value && row.IsNode) && (((short) (row._flags & RowColFlags.Collapsed)) != 0))
                                    {
                                        Point point2 = row.Node.GetRowSpan();
                                        if (point2.Y >= 0)
                                        {
                                            i = point2.Y;
                                        }
                                    }
                                }
                            }
                            else
                            {
                                for (int j = rowSpan.Y; j >= rowSpan.X; j--)
                                {
                                    C1.Win.C1FlexGrid.Row row2 = rows[j];
                                    row2.SetFlags(RowColFlags.Hidden, value);
                                    if ((!value && row2.IsNode) && (((short) (row2._flags & RowColFlags.Collapsed)) != 0))
                                    {
                                        Point point3 = row2.Node.GetRowSpan();
                                        if (point3.X >= 0)
                                        {
                                            j = point3.X;
                                        }
                                    }
                                }
                            }
                            rows.SetDirty();
                            model.NotifyViews(GridChangedTypeEnum.AfterCollapse, safeIndex, -1);
                        }
                    }
                }
            }
        }

        public object Data
        {
            get
            {
                GridModel model = this._row._coll._grid;
                int safeIndex = this._row.SafeIndex;
                int column = model._tree.Column;
                return model.GetData(safeIndex, column);
            }
            set
            {
                GridModel model = this._row._coll._grid;
                int safeIndex = this._row.SafeIndex;
                int column = model._tree.Column;
                model.SetData(safeIndex, column, value);
            }
        }

        public bool Expanded
        {
            get
            {
                return !this.Collapsed;
            }
            set
            {
                this.Collapsed = !value;
            }
        }

        public System.Drawing.Image Image
        {
            get
            {
                GridModel grid = this._row._coll._grid;
                int safeIndex = this._row.SafeIndex;
                int column = grid._tree.Column;
                CellRange range = new CellRange(grid, safeIndex, column);
                return range.Image;
            }
            set
            {
                GridModel grid = this._row._coll._grid;
                int safeIndex = this._row.SafeIndex;
                int column = grid._tree.Column;
                CellRange range = new CellRange(grid, safeIndex, column);
                range.Image = value;
            }
        }

        public object Key
        {
            get
            {
                return this._row._userData;
            }
            set
            {
                this._row._userData = value;
            }
        }

        public int Level
        {
            get
            {
                return this._row._nodeLevel;
            }
            set
            {
                if (this._row._nodeLevel != ((short) value))
                {
                    this._row._nodeLevel = (short) value;
                    this._row._coll.SetDirty();
                    this._row._coll.NotifyViews(GridChangedTypeEnum.RepaintGrid);
                }
            }
        }

        public C1.Win.C1FlexGrid.Row Row
        {
            get
            {
                return this._row;
            }
        }
    }
}

